// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_mesh_data_loader.h"
#include "mk_error.h"
#include "mk_engine.h"

namespace MK3DE
{
	CMeshDataLoader::CMeshDataLoader()
	{

	}

	CMeshDataLoader::~CMeshDataLoader()
	{

	}

	int CMeshDataLoader::LoadMeshData(const BYTE* buffer, DWORD size, MeshData& meshData)
	{
		int ret = 0;
		int result = 1;
		TiXmlDocument* pXML = NULL;
		TiXmlElement* node = NULL;
		TiXmlElement* root = NULL;
		int VertexNum = 0;
		int FaceNum = 0;
		const char* type = NULL;
		int skin = 0;

		pXML = new TiXmlDocument();
		MK_PROCESS_ERROR(pXML);

		pXML->SetCondenseWhiteSpace(false);
		pXML->Parse((const char*)buffer);
		MK_PROCESS_ERROR(pXML->Error() == false);

		root = pXML->RootElement();
		MK_PROCESS_ERROR(root);

		result = strcmp(root->Value(), "Mesh");
		MK_PROCESS_ERROR(result == 0);

		result = root->QueryIntAttribute("VertexNum", &VertexNum);
		MK_PROCESS_ERROR(result == TIXML_SUCCESS);

		result = root->QueryIntAttribute("FaceNum", &FaceNum);
		MK_PROCESS_ERROR(result == TIXML_SUCCESS);

		type = root->Attribute("Type");
		if (strcmp(type, "SkinMesh") == 0)
			skin = 1;
		else
			skin = 0;

		node = root->FirstChildElement("Vertices");
		MK_PROCESS_ERROR(node);

		result = __LoadVertices(node, VertexNum, skin, meshData);
		MK_PROCESS_ERROR(result);

		node = root->FirstChildElement("Face");
		MK_PROCESS_ERROR(node);

		result = __LoadFaces(node, FaceNum, meshData);
		MK_PROCESS_ERROR(result);

		ret = 1;

Exit0:
		SAFE_DELETE(pXML);

		return ret;
	}

	int CMeshDataLoader::__LoadVertices(TiXmlElement* verticesNode, int vertexNum, int skin, MeshData& meshData)
	{
		int ret = 0;
		int result = 0;
		TiXmlElement* node = NULL;

		node = verticesNode->FirstChildElement("Vertex");
		for (int i = 0; i < vertexNum; ++i)
		{
			TiXmlElement* sub_node = NULL;
			CMesh::Position pos;
			CMesh::Normal normal;
			CMesh::TextureCoord uv;

			MK_PROCESS_ERROR(node);

			sub_node = node->FirstChildElement("pos");
			MK_PROCESS_ERROR(sub_node);

			result = sub_node->QueryFloatAttribute("x", &pos.x);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			result = sub_node->QueryFloatAttribute("y", &pos.y);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			result = sub_node->QueryFloatAttribute("z", &pos.z);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			meshData.vecPosition.push_back(pos);

			sub_node = node->FirstChildElement("normal");
			MK_PROCESS_ERROR(sub_node);

			result = sub_node->QueryFloatAttribute("x", &normal.x);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			result = sub_node->QueryFloatAttribute("y", &normal.y);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			result = sub_node->QueryFloatAttribute("z", &normal.z);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			D3DXVec3Normalize(&normal, &normal);
			meshData.vecNormal.push_back(normal);

			sub_node = node->FirstChildElement("uv");
			MK_PROCESS_ERROR(sub_node);

			result = sub_node->QueryFloatAttribute("u", &uv.tu);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			result = sub_node->QueryFloatAttribute("v", &uv.tv);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			meshData.vecTexCoord.push_back(uv);

			if (skin)
			{
				TiXmlElement* boneNode = NULL;
				int idx = 0;

				sub_node = node->FirstChildElement("skin");
				MK_PROCESS_ERROR(sub_node);

				boneNode = sub_node->FirstChildElement("bone");
				MK_PROCESS_ERROR(boneNode);

				CMesh::Influence inf;
				memset(&inf, 0, sizeof(inf));
				inf.boneIdx[0] = inf.boneIdx[1] = inf.boneIdx[2] = inf.boneIdx[3] = 0xff;
				while (boneNode && idx < 4)
				{
					int boneIdx;
					float weight;

					result = boneNode->QueryIntAttribute("boneIdx", &boneIdx);
					MK_PROCESS_ERROR(result == TIXML_SUCCESS);

					result = boneNode->QueryFloatAttribute("weight", &weight);
					MK_PROCESS_ERROR(result == TIXML_SUCCESS);

					inf.boneIdx[idx] = (BYTE)boneIdx;
					inf.weight[idx] = weight;

					boneNode = boneNode->NextSiblingElement("bone");
				}

				meshData.vecInfluence.push_back(inf);
			}

			node = node->NextSiblingElement("Vertex");
		}

		ret = 1;

Exit0:
		return ret;
	}

	int CMeshDataLoader::__LoadFaces(TiXmlElement* faceNode, int faceNum, MeshData& meshData)
	{
		int ret = 0;
		int result = 0;
		TiXmlElement* node = NULL;

		node = faceNode->FirstChildElement("face");
		for (int i = 0; i < faceNum; ++i)
		{
			CMesh::Face face;
			result = node->QueryIntAttribute("x", (int*)&face.vertexIndex[0]);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			result = node->QueryIntAttribute("y", (int*)&face.vertexIndex[1]);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			result = node->QueryIntAttribute("z", (int*)&face.vertexIndex[2]);
			MK_PROCESS_ERROR(result == TIXML_SUCCESS);

			meshData.vecFace.push_back(face);

			node = node->NextSiblingElement("face");
		}

		ret = 1;
Exit0:
		return ret;
	}

	BOOL CMeshDataLoader::SaveToFile(const char* fileName, CMeshDataLoader::MeshData& meshData)
	{
		if (meshData.vecPosition.size() != meshData.vecNormal.size() ||
			meshData.vecPosition.size() != meshData.vecTexCoord.size() ||
			(meshData.vecInfluence.size() && meshData.vecPosition.size() != meshData.vecInfluence.size()))
			return FALSE;
			
		FILE* output = fopen(fileName, "wb");
		if (!output)
			return FALSE;

		CMesh::Header header;
		memset(&header, 0, sizeof(header));
		header.id = CMesh::MK_MESH_ID;
		header.version = CMesh::MK_MESH_VER;
		header.vertexNum = meshData.vecPosition.size();
		header.faceNum = meshData.vecFace.size();

		fseek(output, sizeof(header), SEEK_SET);

		header.offsetPosition = (DWORD)ftell(output);
		fwrite(&meshData.vecPosition[0], sizeof(CMesh::Position), meshData.vecPosition.size(), output);
		
		if (meshData.vecNormal.size() > 0)
		{
			header.offsetNormal = (DWORD)ftell(output);
			fwrite(&meshData.vecNormal[0], sizeof(CMesh::Normal), meshData.vecNormal.size(), output);
		}

		if (meshData.vecTexCoord.size() > 0)
		{
			header.offsetTexCoord1 = (DWORD)ftell(output);
			fwrite(&meshData.vecTexCoord[0], sizeof(CMesh::TextureCoord), meshData.vecTexCoord.size(), output);
		}

		if (meshData.vecInfluence.size() > 0)
		{
			header.offsetSkin = (DWORD)ftell(output);
			fwrite(&meshData.vecInfluence[0], sizeof(CMesh::Influence), meshData.vecInfluence.size(), output);
		}

		if (meshData.vecFace.size() > 0)
		{
			header.offsetFace = (DWORD)ftell(output);
			fwrite(&meshData.vecFace[0], sizeof(CMesh::Face), meshData.vecFace.size(), output);
		}

		fseek(output, 0, SEEK_SET);
		fwrite(&header, sizeof(header), 1, output);

		fclose(output);
		return TRUE;
	}
}
